# src/utils/performance.py
import numpy as np
import pandas as pd
from typing import Dict, List, Tuple, Any
from scipy import stats
import warnings

class PerformanceCalculator:
    """性能指标计算器"""
    
    def __init__(self):
        self.risk_free_rate = 0.03  # 3%无风险利率
    
    def calculate_sharpe_ratio(self, returns: np.ndarray, 
                              risk_free_rate: float = None) -> float:
        """计算Sharpe比率"""
        if risk_free_rate is None:
            risk_free_rate = self.risk_free_rate
        
        excess_returns = returns - risk_free_rate / 252  # 日收益率
        return np.mean(excess_returns) / np.std(excess_returns) * np.sqrt(252)
    
    def calculate_var(self, returns: np.ndarray, confidence_level: float = 0.05) -> float:
        """计算VaR (Value at Risk)"""
        return np.percentile(returns, confidence_level * 100)
    
    def calculate_cvar(self, returns: np.ndarray, confidence_level: float = 0.05) -> float:
        """计算CVaR (Conditional Value at Risk)"""
        var = self.calculate_var(returns, confidence_level)
        return np.mean(returns[returns <= var])
    
    def calculate_maximum_drawdown(self, returns: np.ndarray) -> Tuple[float, int, int]:
        """计算最大回撤"""
        cumulative = np.cumprod(1 + returns)
        running_max = np.maximum.accumulate(cumulative)
        drawdown = (cumulative - running_max) / running_max
        
        max_dd = np.min(drawdown)
        end_idx = np.argmin(drawdown)
        start_idx = np.argmax(running_max[:end_idx])
        
        return max_dd, start_idx, end_idx
    
    def calculate_calmar_ratio(self, returns: np.ndarray) -> float:
        """计算Calmar比率"""
        annual_return = np.mean(returns) * 252
        max_dd, _, _ = self.calculate_maximum_drawdown(returns)
        
        if max_dd == 0:
            return float('inf')
        
        return annual_return / abs(max_dd)
    
    def calculate_sortino_ratio(self, returns: np.ndarray, 
                               target_return: float = 0) -> float:
        """计算Sortino比率"""
        excess_returns = returns - target_return / 252
        downside_returns = excess_returns[excess_returns < 0]
        
        if len(downside_returns) == 0:
            return float('inf')
        
        downside_deviation = np.std(downside_returns) * np.sqrt(252)
        return np.mean(excess_returns) * 252 / downside_deviation
    
    def calculate_information_ratio(self, portfolio_returns: np.ndarray,
                                  benchmark_returns: np.ndarray) -> float:
        """计算信息比率"""
        active_returns = portfolio_returns - benchmark_returns
        tracking_error = np.std(active_returns) * np.sqrt(252)
        
        if tracking_error == 0:
            return 0
        
        return np.mean(active_returns) * 252 / tracking_error
    
    def calculate_beta(self, portfolio_returns: np.ndarray,
                      market_returns: np.ndarray) -> float:
        """计算Beta系数"""
        covariance = np.cov(portfolio_returns, market_returns)[0, 1]
        market_variance = np.var(market_returns)
        
        if market_variance == 0:
            return 0
        
        return covariance / market_variance
    
    def calculate_treynor_ratio(self, portfolio_returns: np.ndarray,
                               market_returns: np.ndarray,
                               risk_free_rate: float = None) -> float:
        """计算Treynor比率"""
        if risk_free_rate is None:
            risk_free_rate = self.risk_free_rate
        
        beta = self.calculate_beta(portfolio_returns, market_returns)
        
        if beta == 0:
            return float('inf')
        
        excess_return = np.mean(portfolio_returns) * 252 - risk_free_rate
        return excess_return / beta
    
    def calculate_portfolio_metrics(self, weights: np.ndarray,
                                   returns: np.ndarray,
                                   cov_matrix: np.ndarray,
                                   esg_scores: np.ndarray = None) -> Dict[str, float]:
        """计算投资组合指标"""
        # 基本指标
        portfolio_return = np.dot(weights, np.mean(returns, axis=0)) * 252
        portfolio_variance = np.dot(weights.T, np.dot(cov_matrix, weights)) * 252
        portfolio_volatility = np.sqrt(portfolio_variance)
        
        # 风险调整收益指标
        sharpe_ratio = (portfolio_return - self.risk_free_rate) / portfolio_volatility
        
        # 计算组合历史收益序列
        portfolio_returns = np.dot(returns, weights)
        
        # 风险指标
        var_95 = self.calculate_var(portfolio_returns, 0.05)
        cvar_95 = self.calculate_cvar(portfolio_returns, 0.05)
        max_dd, _, _ = self.calculate_maximum_drawdown(portfolio_returns)
        
        # 其他比率
        calmar_ratio = self.calculate_calmar_ratio(portfolio_returns)
        sortino_ratio = self.calculate_sortino_ratio(portfolio_returns)
        
        metrics = {
            'expected_return': portfolio_return,
            'volatility': portfolio_volatility,
            'sharpe_ratio': sharpe_ratio,
            'var_95': var_95,
            'cvar_95': cvar_95,
            'max_drawdown': max_dd,
            'calmar_ratio': calmar_ratio,
            'sortino_ratio': sortino_ratio
        }
        
        # ESG指标
        if esg_scores is not None:
            portfolio_esg = np.dot(weights, esg_scores)
            metrics['esg_score'] = portfolio_esg
        
        return metrics
    
    def backtest_portfolio(self, weights_history: List[np.ndarray],
                          returns_history: np.ndarray,
                          rebalance_frequency: int = 20,
                          transaction_cost: float = 0.001) -> Dict[str, Any]:
        """投资组合回测"""
        n_periods = len(returns_history)
        portfolio_values = [1.0]  # 初始净值
        turnover_rates = []
        
        current_weights = weights_history[0]
        
        for t in range(1, n_periods):
            # 计算当日收益
            daily_return = np.dot(current_weights, returns_history[t])
            
            # 更新净值
            new_value = portfolio_values[-1] * (1 + daily_return)
            
            # 检查是否需要调仓
            if t % rebalance_frequency == 0 and t // rebalance_frequency < len(weights_history):
                new_weights = weights_history[t // rebalance_frequency]
                
                # 计算换手率
                turnover = np.sum(np.abs(new_weights - current_weights))
                turnover_rates.append(turnover)
                
                # 扣除交易成本
                cost = transaction_cost * turnover
                new_value *= (1 - cost)
                
                current_weights = new_weights
            
            portfolio_values.append(new_value)
        
        # 计算回测统计
        portfolio_returns = np.array(portfolio_values[1:]) / np.array(portfolio_values[:-1]) - 1
        
        stats = {
            'total_return': portfolio_values[-1] - 1,
            'annualized_return': (portfolio_values[-1] ** (252 / n_periods)) - 1,
            'volatility': np.std(portfolio_returns) * np.sqrt(252),
            'sharpe_ratio': self.calculate_sharpe_ratio(portfolio_returns),
            'max_drawdown': self.calculate_maximum_drawdown(portfolio_returns)[0],
            'calmar_ratio': self.calculate_calmar_ratio(portfolio_returns),
            'average_turnover': np.mean(turnover_rates) if turnover_rates else 0,
            'portfolio_values': portfolio_values,
            'portfolio_returns': portfolio_returns.tolist()
        }
        
        return stats


